import EventHub from "@konghayao/event-hub";
import MainLine from "./utils/MainLine.js";
import { PoolQueue } from "./utils/PoolQueue.js";
import PoolEvent from "./PoolEvent.js";
import log from "loglevel";

export enum PoolState {
    running,
    stop,
    draining,
}
export class Pool<T extends { index: number | string }> extends EventHub<
    "start" | "release" | "acquire" | "allRelease" | "drain" | "stop" | "clear",
    () => void
> {
    status: PoolState = PoolState.stop; // running draining stop
    _mainLine = new MainLine(); // 总线结构，用于接收用户输入的请求
    resources = new Set<T>();
    borrowed = new Set<T>();
    waitingQueue: ((value: any) => void)[] = [];
    config = {
        waitForApply: false, // 在非正常工作的阶段将会存储这个时候到来的需求，默认返回一个错误。
        autoRun: true,
    };
    log: log.Logger;
    factories: PoolQueue<T>;
    set logLevel(message: log.LogLevelDesc) {
        this.log.setLevel(message);
    }
    constructor({
        waitForApply = false,
        logLevel = "warn",
        /** 生成实例后自动启动 */
        autoRun = true,
        /** 优先级队列的比较方式 */
        ruleOfQueue = null,
    } = {}) {
        super(PoolEvent as any);
        this.factories = new PoolQueue(
            ruleOfQueue ||
                function (a, b) {
                    return a.index < b.index;
                }
        );
        this.log = log.noConflict();
        Object.assign(this, { logLevel });
        Object.assign(this.config, {
            waitForApply,
            autoRun,
        });
        this.status = autoRun ? PoolState["running"] : PoolState["stop"];
    }
    /* 增加一个执行 Resource 任务的对象
     */
    addFactory(factory: T) {
        this.factories.add(factory);
        this.resources.add(factory);
    }
    /* 启动池子，如果池子中有可以 */
    start() {
        return this._mainLine.addEvent(() => {
            if (this.status !== PoolState["running"]) {
                this.status = PoolState["running"];
                super.emit("start");
            }
        });
    }
    async _Waiting() {
        if (this.status !== PoolState["running"]) {
            // 非工作状态处理
            if (!this.config.waitForApply) {
                throw "Your acquire was canceled because waitForApply was set false";
            }
            this.log.warn(`Pool | ${this.status} |: Your acquire was cached`);

            // 等候状态恢复
            return new Promise((resolve) => {
                this.waitingQueue.push(resolve);
            });
        }
        if (this.factories.size === 0) {
            // 不足等待处理
            return new Promise((resolve) => {
                this.waitingQueue.push(resolve);
            });
        }
    }
    /** 从优先级队列中选取出一个资源 */
    async acquire() {
        await this._Waiting();

        const resource = this.factories.poll();
        if (resource === undefined) {
            this.log.error("越前处理");
            throw "PoolQueue acquire can't find a resource";
        }
        this.borrowed.add(resource);
        super.emit("acquire", resource);
        return resource;
    }
    /** 放回一个资源到优先级队列 */
    release(resource: T) {
        if (resource) {
            this.borrowed.delete(resource);
            this.factories.add(resource);
            super.emit("release", resource);
            if (this.borrowed.size === 0) {
                super.emit("allRelease");
            }
        } else {
            this.log.info("release: 放回一个空 resource");
        }
    }
    /** 关闭整个 Pool */
    async drain() {
        this.status = PoolState["draining"];
        this._mainLine.addEvent(() => {
            super.emit("drain");
            // 暂停事件
            if (this.borrowed.size === 0) {
                this.status = PoolState["stop"];
                super.emit("stop");
                return;
            }
            return new Promise((resolve) => {
                super.once("allRelease", () => {
                    this.status = PoolState["stop"];
                    super.emit("stop");
                    resolve(true);
                });
            });
        });
    }
    /** 清空池子 */
    clear() {
        if (this.status === PoolState["stop"]) {
            this.resources.clear();
            this.factories.clear();
            super.emit("clear");
        } else {
            this.log.warn(
                `Pool | ${this.status} |: clear() is only used in status | stop |`
            );
        }
    }
    /** 在启动状态下直接使用函数调用一个 Resource */
    use<Result>(func: (value: T) => Result): Promise<Result> {
        let temp: T;
        return this.acquire()
            .then((factory) => (temp = factory))
            .then(func)
            .then(async (result) => {
                this.release(temp);
                return result;
            });
    }
}
